// @Description
// 使用 context 封装HTML/String/JSON函数, 简化接口的调用
// 更重要的是用来保存与本次请求周期相关的信息, 用于后续扩展
// @Author insight21
// @Since 2021/9/11

package web

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

// 用于构建 JSON 数据
type JSON map[string]interface{}

type Context struct {
	Writer     http.ResponseWriter
	Request    *http.Request
	Method     string
	Path       string
	StatusCode int
	Params     map[string]string
	midHandler []HandlerFunc // 中间件
	midIdx     int           // 中间件的当前索引
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
	return &Context{
		Writer:  w,
		Request: req,
		Method:  req.Method,
		Path:    req.URL.Path,
		midIdx:  -1,
	}
}

func (c *Context) setStatusCode(code int) {
	c.StatusCode = code
	c.Writer.WriteHeader(code)
}

func (c *Context) setHeader(key, value string) {
	c.Writer.Header().Set(key, value)
}

// 访问 Form 内容
func (c *Context) PostForm(key string) string {
	return c.Request.FormValue(key)
}

// 访问 Query 内容
func (c *Context) Query(key string) string {
	return c.Request.URL.Query().Get(key)
}

// 使用自定义 format 格式获取返回信息
func (c *Context) String(code int, format string, value ...interface{}) {
	c.setStatusCode(code)
	c.setHeader("Content-Type", "text/plain")
	_, err := c.Writer.Write([]byte(fmt.Sprintf(format, value...)))
	if err != nil {
		panic(err)
	}
}

// 返回 JSON 格式数据
func (c *Context) JSON(code int, value ...interface{}) {
	c.setStatusCode(code)
	c.setHeader("Content-Type", "application/json")
	encoder := json.NewEncoder(c.Writer)
	if err := encoder.Encode(value); err != nil {
		panic(err)
	}
}

// 返回字节数据
func (c *Context) Data(code int, data []byte) {
	c.setStatusCode(code)
	_, err := c.Writer.Write(data)
	if err != nil {
		panic(err)
	}
}

// 返回 HTML
func (c *Context) HTML(code int, html string) {
	c.setStatusCode(code)
	c.setHeader("Content-Type", "application/html")
	_, err := c.Writer.Write([]byte(html))
	if err != nil {
		panic(err)
	}
}

// 访问 Params
func (c *Context) Param(key string) string {
	param, ok := c.Params[key]
	if !ok {
		log.Printf("Find Param key: %v error!", key)
	}
	return param
}

// 依次调用所有中间件
func (c *Context) Next() {
	c.midIdx++
	size := len(c.midHandler)
	for ; c.midIdx < size; c.midIdx++ {
		curMidHandler := c.midHandler[c.midIdx]
		curMidHandler(c)
	}
}

// 返回失败信息
func (c *Context) Fail(code int, err string) {
	c.midIdx = len(c.midHandler)
	c.JSON(code, err)
}
